home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ShareWare OnLine 2
/
ShareWare OnLine Volume 2 (CMS Software)(1993).iso
/
util2
/
pgp22src.zip
/
SRC
/
R3000.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-07
|
9KB
|
372 lines
/* r3000.c - MIPS R3000 adaptation of selected routines from mpilib.c.
C source code for multiprecision arithmetic library routines.
R3000 optimizations by Castor Fu, in Sep 1992.
See the comments in the file mpilib.c for details on the purpose of
these functions.
Original version of mpilib.c implemented in 1986 by
Philip R. Zimmermann, updated by PRZ 1990-1992.
Boulder Software Engineering
3021 Eleventh Street
Boulder, CO 80304
(303) 541-0140
(c) Copyright 1986-92 by Philip Zimmermann. All rights reserved.
The author assumes no liability for damages resulting from the use
of this software, even if the damage results from defects in this
software. No warranty is expressed or implied.
-- Adapt so that the unit size can be a full int size. This
was inspired by the lack of a carry bit on MIPSco processors.
On such machines, the advantage of assembly language coding
is less clear. (Except for the multiply. . . )
One reason for creating an R3000 module of C routines is that
we have one routine (P_ROTL) which is compiled particularly
poorly, and chose to explicitly unroll the loop.
*/
#include "mpilib.h"
#define word_v(r,n) (r)[tohigher(n)]
extern short global_precision; /* units of precision for all routines */
/* global_precision is the unit precision last set by set_precision.
Initially, set_precision() should be called to define global_precision
before using any of these other multiprecision library routines.
i.e.: set_precision(MAX_UNIT_PRECISION);
*/
/*************** multiprecision library primitives ****************/
/* The following portable C primitives should be recoded in assembly.
The equivalent assembly primitives are externally defined under
different names, unless PORTABLE is defined. See the header file
"mpilib.h" for further details.
*/
typedef unsigned long int ulint;
/* ...assumes sizeof(unit) <= sizeof(unsigned long) */
boolean mp_addc
(register unitptr r1,register unitptr r2,register boolean carry)
/* multiprecision add with carry r2 to r1, result in r1 */
/* carry is incoming carry flag-- value should be 0 or 1 */
{ register unit x,x1;
int i;
short precision; /* number of units to add */
unsigned int mcarry = carry;
precision = global_precision;
make_lsbptr(r1,precision);
make_lsbptr(r2,precision);
i = precision&3;
while (i--)
{
if (mcarry) {
x = *r1 + *r2 + 1;
x1 = ~ *r1;
mcarry = 1 ^ (*r2 < x1);
} else {
x = *r1 + *r2;
mcarry = (x < *r1) ;
}
post_higherunit(r2);
*post_higherunit(r1) = x;
}
i = precision>>2;
while (i--)
{
#define ADDC(n) \
if (mcarry) { \
x = word_v(r1,n) + word_v(r2,n) + 1; \
x1 = ~word_v(r1,n); \
mcarry = 1 ^ (word_v(r2,n) < x1); \
} else { \
x = word_v(r1,n) + word_v(r2,n); \
mcarry = (x < word_v(r1,n)) ; \
} \
word_v(r1,n) = x;
ADDC(0);
ADDC(1);
ADDC(2);
ADDC(3);
#undef ADDC
r1 += tohigher(4);
r2 += tohigher(4);
}
return(mcarry); /* return the final carry flag bit */
} /* mp_addc */
boolean mp_subb
(register unitptr r1,register unitptr r2,register boolean borrow)
/* multiprecision subtract with borrow, r2 from r1, result in r1 */
/* borrow is incoming borrow flag-- value should be 0 or 1 */
{ register unit x;
unsigned int mborrow = borrow;
int i;
short precision; /* number of units to subtract */
precision = global_precision;
make_lsbptr(r1,precision);
make_lsbptr(r2,precision);
i = precision&3;
while (i--)
{ if (mborrow) {
x = *r1 - *r2 - mborrow;
mborrow = 1 ^ (*r2 < *r1);
} else {
x = *r1 - *r2;
mborrow = (*r1 < *r2);
}
post_higherunit(r2);
*post_higherunit(r1) = x;
}
i = precision>>2;
while (i--)
{
#define SUBB(n) \
if (mborrow) { \
x = word_v(r1,n) - word_v(r2,n) - mborrow; \
mborrow = 1 ^ (word_v(r2,n) < word_v(r1,n)); \
} else { \
x = word_v(r1,n) - word_v(r2,n); \
mborrow = (word_v(r1,n) < word_v(r2,n)); \
} \
word_v(r1,n) = x;
SUBB(0);
SUBB(1);
SUBB(2);
SUBB(3);
#undef SUBB
r1 += tohigher(4);
r2 += tohigher(4);
}
return(mborrow); /* return the final carry/borrow flag bit */
} /* mp_subb */
/* We've unrolled the loop here because the MIPS/DEC C compiler is too
* stupid to do so. Presumably on register poor machines this is not
* a clearly good idea
*/
boolean mp_rotate_left(register unitptr r1,register boolean carry)
/* multiprecision rotate left 1 bit with carry, result in r1. */
/* carry is incoming carry flag-- value should be 0 or 1 */
{ register int precision; /* number of units to rotate */
unsigned int mcarry = carry, carry2,carry3,carry4, nextcarry;
int i;
precision = global_precision;
make_lsbptr(r1,precision);
i = precision &3;
while (i--) {
nextcarry = (((signedunit) *r1) < 0);
*r1 = (*r1 << 1) | mcarry;
mcarry = nextcarry;
pre_higherunit(r1);
}
i = precision>>2;
while (i--)
{
carry2 = (((signedunit) *r1) < 0);
*r1 = (*r1 << 1) | mcarry;
carry3 = (((signedunit) word_v(r1,1)) < 0);
word_v(r1,1) = (word_v(r1,1) << 1) | carry2;
carry4 = (((signedunit) word_v(r1,2)) < 0);
word_v(r1,2) = (word_v(r1,2) << 1) | carry3;
mcarry = (((signedunit) word_v(r1,3)) < 0);
word_v(r1,3) = (word_v(r1,3) << 1) | carry4;
r1 += tohigher(4);
}
return(mcarry); /* return the final carry flag bit */
} /* mp_rotate_left */
void p_setp(short nbits){} ;
/************** end of primitives that should be in assembly *************/
#include "lmul.h" /* used only by R3000.c */
#ifdef MUNIT16
typedef unsigned short MULTUNIT;
#endif
#ifdef MUNIT32
typedef unsigned int MULTUNIT;
#endif
#define MULTUNITSIZE (sizeof(MULTUNIT)*8)
#define MULTUNIT_hmask ((1UL << (MULTUNITSIZE/2))-1)
#define MULTUNIT_mask ((MULTUNIT_hmask<<(MULTUNITSIZE/2)) | MULTUNIT_hmask)
void p_smula (
MULTUNIT *prod,
MULTUNIT *multiplicand,
MULTUNIT multiplier)
{
short i=global_precision;
int count=i,j;
MULTUNIT *pp=prod, *mp=multiplicand;
MULTUNIT pl, ph, npl, nph, cl, ch;
MULTUNIT al, ah;
cl = 0;
ch = 0;
if (i <= 0 ) return;
lmul(multiplier, *multiplicand, pl, ph);
post_higherunit(multiplicand);
al = 0;
ah = 0;
ch = 0;
while(--i) {
lmul(multiplier, *multiplicand, npl, nph);
post_higherunit(multiplicand);
al += *prod;
cl = (al < *prod);
al += pl;
cl += (al < pl);
ah += ph;
ch = (ah < ph);
ah += cl;
ch += (ah < cl);
*prod = al;
post_higherunit(prod);
al = ah;
ah = ch;
pl = npl;
ph = nph;
}
al += *prod;
cl = (al < *prod);
al += pl;
cl += (al < pl);
ah += ph;
ch = (ah < ph);
ah += cl;
ch += (ah < cl);
*prod = al;
post_higherunit(prod);
*prod += ah;
ch += (*prod < ah);
post_higherunit(prod);
/*
*prod += ch ;
post_higherunit(prod);
*/
} /* mp_smul */
static int mshift; /* number of bits for
** recip scaling */
static MULTUNIT reciph; /* MSunit of scaled recip */
static MULTUNIT recipl; /* LSunit of scaled recip */
void p_setrecip(MULTUNIT rh, MULTUNIT rl, int m)
{
reciph = rh;
recipl = rl;
mshift = m;
}
MULTUNIT p_quo_digit (MULTUNIT *dividend)
{
MULTUNIT ql, qh, q0l, q0h, q1l, q1h, q2l, q2h;
MULTUNIT q3h, q3l, q4h, q4l;
MULTUNIT mutemp;
/* Compute the least significant product group.
The last terms of q1 and q2 perform upward rounding, which is
needed to guarantee that the result not be too small.
*/
lmul(dividend[tohigher(-2)] ^ MULTUNIT_mask, reciph, q1l, q1h);
q1l += reciph;
q1h += (q1l < reciph);
lmul(dividend[tohigher(-1)]^ MULTUNIT_mask, recipl, q2l, q2h);
q2h += 1;
q1l = (q1l >> 1) + (q1h << (MULTUNITSIZE-1));
q1h >>= 1;
q2l = (q2l >> 1) + (q2h << (MULTUNITSIZE-1));
q2h >>= 1;
q0l = q1l + q2l;
q0h = q1h + q2h + (q0l < q1l);
q0l++;
q0h+= (q0l < 1);
/* Compute the middle significant product group. */
lmul(dividend[tohigher(-1)]^MULTUNIT_mask, reciph, q3l, q3h);
lmul(dividend[0] ^ MULTUNIT_mask, recipl, q4l, q4h);
q3l = (q3l >> 1) + (q3h << (MULTUNITSIZE-1));
q3h >>= 1;
q4l = (q4l >> 1) + (q4h << (MULTUNITSIZE-1));
q4h >>= 1;
ql = q0h + 1;
qh = (ql < 1);
ql += q3l;
qh += q3h + (ql < q3l);
ql += q4l;
qh += q4h + (ql < q4l);
/* Compute the most significant term and add in the others */
lmul((dividend[0] ^ MULTUNIT_mask), reciph, q1l, q1h);
q1h = (q1h << 1) + (q1l>>(MULTUNITSIZE-1));
q1l = (q1l << 1) ;
ql = (ql >> (MULTUNITSIZE-2)) + (qh << 2);
qh >>= (MULTUNITSIZE-2);
ql += q1l;
qh += (ql < q1l) + q1h;
if (mshift != MULTUNITSIZE) {
ql = (ql >> mshift) + (qh << (MULTUNITSIZE-mshift));
qh >>= mshift;
} else {
ql = qh;
qh = 0;
}
/* Prevent overflow and then wipe out the intermediate results. */
mutemp = (qh != 0) ? MULTUNIT_mask : ql;
return(mutemp);
}
/* end of r3000.c */